﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Pavilion
{
    public class CollisionManager
    {
        static private CollisionManager singletonInstance;
        static public CollisionManager Instance { get { return (singletonInstance == null) ? new CollisionManager() : singletonInstance; } }

        private List<MultiLinePhysicalData> multiLineDataCollection;
        private List<PolygonPhysicalData> polygonDataCollection;

        private List<PolygonAndMultiLineIntersection> polygonAndMultiLineIntersectionList;
        private List<PolygonAndPolygonIntersection> polygonAndPolygonIntersectionList;

        private Dictionary<PolygonPhysicalData, MultiLineCollisionData> polygonToMultiLinesDictionary;
        private Dictionary<MultiLinePhysicalData, PolygonCollisionData> multiLineToPolygonsDictionary;
        private Dictionary<PolygonPhysicalData, PolygonCollisionData> polygonToPolygonsDictaionary;

        public PolygonCollisionData GetPolygonToPolygonsData(PolygonPhysicalData polygonData) { return polygonToPolygonsDictaionary[polygonData]; }
        public MultiLineCollisionData GetPolygonToMultiLinesData(PolygonPhysicalData polygonData) { return polygonToMultiLinesDictionary[polygonData]; }

        private CollisionManager()
        {
            singletonInstance = this;

            multiLineDataCollection = new List<MultiLinePhysicalData>();
            polygonDataCollection = new List<PolygonPhysicalData>();

            polygonAndMultiLineIntersectionList = new List<PolygonAndMultiLineIntersection>();
            polygonAndPolygonIntersectionList = new List<PolygonAndPolygonIntersection>();

            polygonToMultiLinesDictionary = new Dictionary<PolygonPhysicalData, MultiLineCollisionData>();
            multiLineToPolygonsDictionary = new Dictionary<MultiLinePhysicalData, PolygonCollisionData>();
            polygonToPolygonsDictaionary = new Dictionary<PolygonPhysicalData, PolygonCollisionData>();
        }

        internal void Clear()
        {
            multiLineDataCollection.Clear();
            polygonDataCollection.Clear();

            polygonAndMultiLineIntersectionList.Clear();
            polygonAndPolygonIntersectionList.Clear();

            polygonToMultiLinesDictionary.Clear();
            multiLineToPolygonsDictionary.Clear();
            polygonToPolygonsDictaionary.Clear();
        }

        internal void Update()
        {
            foreach (PolygonAndMultiLineIntersection intersection in polygonAndMultiLineIntersectionList)
                intersection.UpdateIntersection();

            foreach (PolygonAndPolygonIntersection intersection in polygonAndPolygonIntersectionList)
                intersection.UpdateIntersection();

            foreach (MultiLineCollisionData collisionData in polygonToMultiLinesDictionary.Values)
                collisionData.NotifyIntersectionChanged();

            foreach (PolygonCollisionData collisionData in multiLineToPolygonsDictionary.Values)
                collisionData.NotifyIntersectionChanged();

            foreach (PolygonCollisionData collisionData in polygonToPolygonsDictaionary.Values)
                collisionData.NotifyIntersectionChanged();
        }

        public void AddPolygonData(PolygonPhysicalData polygonData)
        {
            polygonDataCollection.Add(polygonData);

            RegisterPolygonToMultilines(polygonData);
            RegisterPolygonToPolygons(polygonData);            
        }

        public void AddMultiLineData(MultiLinePhysicalData multiLineData)
        {
            multiLineDataCollection.Add(multiLineData);

            RegisterMultilineToPolygon(multiLineData);
        }

        public void RemovePolygonData(PolygonPhysicalData polygonData)
        {
            UnregisterPolygonToPolygons(polygonData);
            UnregisterPolygonToMultilines(polygonData);

            polygonDataCollection.Remove(polygonData);
        }

        public void RemoveMultiLineData(MultiLinePhysicalData multiLineData)
        {
            UnregisterMultilineToPolygon(multiLineData);
            multiLineDataCollection.Remove(multiLineData);
        }

        public void RegisterToMultiLineData(MultiLinePhysicalData multiLineData, Action<PolygonCollisionData> polygonAndMultiLineCollisionChangedAction)
        {
            multiLineToPolygonsDictionary[multiLineData].RegisterToCollisionChanged(polygonAndMultiLineCollisionChangedAction);
        }

        public void RegisterToPolygonData(PolygonPhysicalData polygonData, Action<MultiLineCollisionData> polygonAndMultiLineCollisionChangedAction)
        {
            polygonToMultiLinesDictionary[polygonData].RegisterToCollisionChanged(polygonAndMultiLineCollisionChangedAction);
        }

        public void RegisterToPolygonData(PolygonPhysicalData polygonData, Action<PolygonCollisionData> polygonAndPolygonCollisionChangedAction)
        {
            polygonToPolygonsDictaionary[polygonData].RegisterToCollisionChanged(polygonAndPolygonCollisionChangedAction);
        }

        private void RegisterPolygonToMultilines(PolygonPhysicalData polygonData)
        {
            polygonToMultiLinesDictionary.Add(polygonData, new MultiLineCollisionData(polygonData));

            foreach (MultiLinePhysicalData multiLineData in multiLineDataCollection)
                polygonAndMultiLineIntersectionList.Add(new PolygonAndMultiLineIntersection(polygonToMultiLinesDictionary[polygonData], multiLineToPolygonsDictionary[multiLineData]));
        }

        private void UnregisterPolygonToMultilines(PolygonPhysicalData polygonData)
        {
            MultiLineCollisionData multiLineCollisionData = polygonToMultiLinesDictionary[polygonData];
            IEnumerable<PolygonAndMultiLineIntersection> intersectionsToRemove = polygonAndMultiLineIntersectionList.Where(x => x.MultiLineCollisionData == multiLineCollisionData).ToArray();

            foreach (PolygonAndMultiLineIntersection intersection in intersectionsToRemove)
                polygonAndMultiLineIntersectionList.Remove(intersection);

            polygonToMultiLinesDictionary.Remove(polygonData);
            multiLineCollisionData.Dispose();
        }

        private void RegisterPolygonToPolygons(PolygonPhysicalData polygonData)
        {
            polygonToPolygonsDictaionary.Add(polygonData, new PolygonCollisionData(polygonData));

            foreach (PolygonPhysicalData item in polygonDataCollection.Where(x => x != polygonData))
                polygonAndPolygonIntersectionList.Add(new PolygonAndPolygonIntersection(polygonToPolygonsDictaionary[polygonData], polygonToPolygonsDictaionary[item]));
        }

        private void UnregisterPolygonToPolygons(PolygonPhysicalData polygonData)
        {
            PolygonCollisionData polygonCollisionData = polygonToPolygonsDictaionary[polygonData];
            IEnumerable<PolygonAndPolygonIntersection> intersectionsToRemove = polygonAndPolygonIntersectionList.Where(x => x.CollisionDataA == polygonCollisionData || x.CollisionDataB == polygonCollisionData).ToArray();

            foreach (PolygonAndPolygonIntersection intersection in intersectionsToRemove)
                polygonAndPolygonIntersectionList.Remove(intersection);

            polygonToPolygonsDictaionary.Remove(polygonData);
            polygonCollisionData.Dispose();
        }

        private void RegisterMultilineToPolygon(MultiLinePhysicalData multiLineData)
        {
            multiLineToPolygonsDictionary.Add(multiLineData, new PolygonCollisionData(multiLineData));

            foreach (PolygonPhysicalData polygonData in polygonDataCollection)
                polygonAndMultiLineIntersectionList.Add(new PolygonAndMultiLineIntersection(polygonToMultiLinesDictionary[polygonData], multiLineToPolygonsDictionary[multiLineData]));
        }

        private void UnregisterMultilineToPolygon(MultiLinePhysicalData multiLineData)
        {
            PolygonCollisionData polygonCollisionData = multiLineToPolygonsDictionary[multiLineData];
            IEnumerable<PolygonAndMultiLineIntersection> intersectionsToRemove = polygonAndMultiLineIntersectionList.Where(x => x.PolygonCollisionData == polygonCollisionData).ToArray();

            foreach (PolygonAndMultiLineIntersection intersection in intersectionsToRemove)
                polygonAndMultiLineIntersectionList.Remove(intersection);

            multiLineToPolygonsDictionary.Remove(multiLineData);
            polygonCollisionData.Dispose();
        }
    }

    public class CollisionManager2
    {
        static private CollisionManager2 singletonInstance;

        private Dictionary<MultiLinePhysicalData, PolygonAndMultiLineIntersectionController> multiLineDataCollection;
        private List<PolygonPhysicalData> polygonDataCollection;

        private List<PolygonAndMultiLineIntersectionData> polygonAndMultiLineIntersectionList;
        private List<PolygonAndPolygonIntersection> polygonAndPolygonIntersectionList;

        private Dictionary<PolygonPhysicalData, MultiLineCollisionData> polygonToMultiLinesDictionary;
        private Dictionary<MultiLinePhysicalData, PolygonCollisionData> multiLineToPolygonsDictionary;
        private Dictionary<PolygonPhysicalData, PolygonCollisionData> polygonToPolygonsDictaionary;

        public PolygonCollisionData GetPolygonToPolygonsData(PolygonPhysicalData polygonData) { return polygonToPolygonsDictaionary[polygonData]; }
        public MultiLineCollisionData GetPolygonToMultiLinesData(PolygonPhysicalData polygonData) { return polygonToMultiLinesDictionary[polygonData]; }

        private CollisionManager2()
        {
            singletonInstance = this;

            multiLineDataCollection = new Dictionary<MultiLinePhysicalData, PolygonAndMultiLineIntersectionController>();
            polygonDataCollection = new List<PolygonPhysicalData>();

            polygonAndMultiLineIntersectionList = new List<PolygonAndMultiLineIntersectionData>();
            polygonAndPolygonIntersectionList = new List<PolygonAndPolygonIntersection>();

            polygonToMultiLinesDictionary = new Dictionary<PolygonPhysicalData, MultiLineCollisionData>();
            multiLineToPolygonsDictionary = new Dictionary<MultiLinePhysicalData, PolygonCollisionData>();
            polygonToPolygonsDictaionary = new Dictionary<PolygonPhysicalData, PolygonCollisionData>();
        }

        static public CollisionManager2 Instance
        {
            get
            {
                if (singletonInstance == null)
                    new CollisionManager2();

                return singletonInstance;
            }
        }

        internal void Update()
        {
            //foreach (PolygonAndMultiLineIntersection intersection in polygonAndMultiLineIntersectionList)
            //    intersection.UpdateIntersection();

            //foreach (PolygonAndPolygonIntersection intersection in polygonAndPolygonIntersectionList)
            //    intersection.UpdateIntersection();

            //foreach (MultiLineCollisionData collisionData in polygonToMultiLinesDictionary.Values)
            //    collisionData.NotifyIntersectionChanged();

            //foreach (PolygonCollisionData collisionData in multiLineToPolygonsDictionary.Values)
            //    collisionData.NotifyIntersectionChanged();

            //foreach (PolygonCollisionData collisionData in polygonToPolygonsDictaionary.Values)
            //    collisionData.NotifyIntersectionChanged();
        }

        public void AddPolygonData(PolygonPhysicalData polygonData, Action<MultiLineCollisionData> polygonAndMultiLineCollisionChangedAction, Action<PolygonCollisionData> polygonAndPolygonCollisionChangedAction)
        {
            //polygonToPolygonsDictaionary.Add(polygonData, new PolygonCollisionData(polygonData, polygonAndPolygonCollisionChangedAction));
            //polygonToMultiLinesDictionary.Add(polygonData, new MultiLineCollisionData(polygonData, polygonAndMultiLineCollisionChangedAction));
            polygonDataCollection.Add(polygonData);

            foreach (MultiLinePhysicalData multiLineData in multiLineDataCollection.Keys)
                polygonAndMultiLineIntersectionList.Add(new PolygonAndMultiLineIntersectionData(polygonData, multiLineData));

            foreach (PolygonPhysicalData item in polygonDataCollection)
            {
                if (item != polygonData)
                    polygonAndPolygonIntersectionList.Add(new PolygonAndPolygonIntersection(polygonToPolygonsDictaionary[polygonData], polygonToPolygonsDictaionary[item]));
            }
        }

        public void AddMultiLineData(MultiLinePhysicalData multiLineData, Action<PolygonAndMultiLineIntersectionController> polygonAndMultiLineCollisionChangedAction)
        {
            multiLineDataCollection.Add(multiLineData, new PolygonAndMultiLineIntersectionController());

            foreach (PolygonPhysicalData polygonData in polygonDataCollection)
                polygonAndMultiLineIntersectionList.Add(new PolygonAndMultiLineIntersectionData(polygonData, multiLineData));
        }
    }

    public class PolygonAndMultiLineIntersectionController
    {

    }

    public class PolygonAndMultiLineIntersectionData
    {
        private Dictionary<LineVector, LineIntersectionData> intersectedData;
        private PolygonPhysicalData PolygonData { get; set; }
        private MultiLinePhysicalData MultiLineData { get; set; }
        private bool checkIntersection;

        public PolygonAndMultiLineIntersectionData(PolygonPhysicalData physicalData, MultiLinePhysicalData multiLineData)
        {
            this.checkIntersection = true;
            this.PolygonData = physicalData;
            this.MultiLineData = multiLineData;

            PolygonData.PhysicalFormChanged += new Action<PhysicalData>(PhysicalData_PhysicalFormChanged);
            MultiLineData.PhysicalFormChanged += new Action<PhysicalData>(PhysicalData_PhysicalFormChanged);

            intersectedData = new Dictionary<LineVector, LineIntersectionData>();
        }

        internal void UpdateIntersection()
        {
            if (!checkIntersection)
                return;

            checkIntersection = false;

            foreach (LineVector line in MultiLineData.LineCollection)
            {
                IEnumerable<LineVector> lineIntersections = LineVector.ObtainLineIntersactions(PolygonData.PhysicalBounds, line);
                if (lineIntersections != null)
                {
                    LineIntersectionData lineIntersectionData = new LineIntersectionData();
                    lineIntersectionData.Line = line;
                    lineIntersectionData.PolygonLines.AddRange(lineIntersections);

                    intersectedData.Add(line, lineIntersectionData);
                }
                else
                {
                    intersectedData.Remove(line);
                }
            }
        }

        private void PhysicalData_PhysicalFormChanged(PhysicalData physicalData) { checkIntersection = true; }
    }

    public class LineIntersectionData
    {
        public LineVector Line { get; set; }
        public List<LineVector> PolygonLines { get; private set; }

        public LineIntersectionData()
        {
            PolygonLines = new List<LineVector>();
        }
    }
}
